﻿''' <summary>
''' 多栏comboBox列
''' 要求数据源为DataTable或者集合成员实现了IList接口
''' </summary>
Public Class DataGridViewMultiColumnComboBoxColumn
    Inherits DataGridViewComboBoxColumn

    Private m_ColumnWidths As String '栏目宽度字串：100;150;200等
    Private m_ColumnTitles As String '栏目标题字串：Name;Code;Sexy等
    Private m_ColumnNames As String '栏目成员名称：Name;Code;Sexy等,如果是类，则是类中属性，如果是table，则是字段名称
    Private m_ColumnIndexs As String '栏目成员所在索引IList接口实现；如0;1;3等
    Private m_SelectedValue2 As Object '所选行的第二个返回值。由第二个返回索引指定,此属性和下面属性配合使用，在EditingControl的selectedItem中赋值
    Private m_SelectedIndex2 As Int32 '所选行的第二个返回值索引。

    Private m_SelectedIndex As Int32 '保存editingControl选择的索引

    Public Delegate Sub HotKeyCallBack()
    Shared m_DelegateCallBack As HotKeyCallBack

    Public Sub New()
        'MyBase.New(New DataGridViewMultiColumnComboBoxCell)
        Me.CellTemplate = New DataGridViewMultiColumnComboBoxCell()
        m_SelectedIndex2 = -1
        Me.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing

        'Debug.Print("New, cellTemplate = ")

    End Sub

    'Public Overrides 

    'CellTemplate
    Public Overrides Property CellTemplate As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(value As DataGridViewCell)
            '确保value是datagridviewmulticolumncomboboxcell类
            If (value IsNot Nothing) AndAlso
                Not value.GetType().IsAssignableFrom(GetType(DataGridViewMultiColumnComboBoxCell)) _
                Then
                Throw New InvalidCastException("Must be a DataGridViewMultiColumnComboBoxCell")
            End If
            MyBase.CellTemplate = value
        End Set
    End Property

    <System.ComponentModel.Browsable(True), System.ComponentModel.Description("各栏的宽度值，以分号分隔。")>
    Public Property ColumnWidths As String
        Get
            Return m_ColumnWidths
        End Get
        Set(value As String)
            m_ColumnWidths = value
        End Set
    End Property

    <System.ComponentModel.Browsable(True), System.ComponentModel.Description("各栏的标题，以分号分隔。")>
    Public Property ColumnTitles As String
        Get
            Return m_ColumnTitles
        End Get
        Set(value As String)
            m_ColumnTitles = value
        End Set
    End Property

    Public Property ColumnNames As String
        Get
            Return m_ColumnNames
        End Get
        Set(value As String)
            m_ColumnNames = value
        End Set
    End Property

    Public Property ColumnIndexs As String
        Get
            Return m_ColumnIndexs
        End Get
        Set(value As String)
            m_ColumnIndexs = value
        End Set
    End Property

    Public Property SelectedValue2 As Object
        Get
            Return m_SelectedValue2
        End Get
        Set(value As Object)
            m_SelectedValue2 = value
        End Set
    End Property

    Public Property SelectedIndex2 As Integer
        Get
            Return m_SelectedIndex2
        End Get
        Set(value As Integer)
            m_SelectedIndex2 = value
        End Set
    End Property

    Public Property SelectedIndex As Integer
        Get
            Return m_SelectedIndex
        End Get
        Set(value As Integer)
            m_SelectedIndex = value
        End Set
    End Property

    Public Shared Property DelegateCallBack As HotKeyCallBack
        Get
            Return m_DelegateCallBack
        End Get
        Set(value As HotKeyCallBack)
            m_DelegateCallBack = value
        End Set
    End Property
End Class

Public Class DataGridViewMultiColumnComboBoxCell
    Inherits DataGridViewComboBoxCell

    'EditType
    Public Overrides ReadOnly Property EditType As Type
        Get
            'Return MyBase.EditType
            '返回单元格使用的 编辑控件
            Return GetType(DataGridViewMultiColumnComboBoxEditingControl)
        End Get
    End Property



    'InitializeEditingControl
    Public Overrides Sub InitializeEditingControl(rowIndex As Integer, initialFormattedValue As Object, dataGridViewCellStyle As DataGridViewCellStyle)
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)

        'Debug.Print("InitializeEditingControl")

        Dim editCtl As DataGridViewMultiColumnComboBoxEditingControl = CType(DataGridView.EditingControl, DataGridViewMultiColumnComboBoxEditingControl)
        '把当前单元格赋值给编辑控件的属性
        editCtl.OwnerCell = Me
        editCtl.FlatStyle = FlatStyle.Popup
        editCtl.DropDownStyle = ComboBoxStyle.DropDown
        editCtl.AutoCompleteMode = AutoCompleteMode.Append  'AutoCompleteMode.Suggest
    End Sub
End Class

Public Class DataGridViewMultiColumnComboBoxEditingControl
    Inherits DataGridViewComboBoxEditingControl

    Private m_OwnerCell As DataGridViewMultiColumnComboBoxCell
    Private m_currentIndex As Integer '当前光标所在行索引
    Private m_acctList As List(Of ClsTAccount) '当前所在栏目的datasource

    Public Sub New()

        '自画控件
        Me.DrawMode = DrawMode.OwnerDrawFixed
        'Me.DropDownStyle = ComboBoxStyle.Simple
    End Sub

    Protected Overrides Sub OnGotFocus(e As EventArgs)
        MyBase.OnGotFocus(e)

        If Not Me.DroppedDown Then
            Me.DroppedDown = True
        End If
    End Sub

    Public Property OwnerCell As DataGridViewMultiColumnComboBoxCell
        Get
            Return m_OwnerCell
        End Get
        Set(value As DataGridViewMultiColumnComboBoxCell)
            m_OwnerCell = value
        End Set
    End Property

    Protected Overrides Sub OnSelectedValueChanged(e As EventArgs)
        MyBase.OnSelectedValueChanged(e)

        'Debug.Print("OnSelectedValueChanged")
    End Sub

    Protected Overrides Sub OnSelectionChangeCommitted(e As EventArgs)
        MyBase.OnSelectionChangeCommitted(e)

        'Debug.Print("onSelectedChangeCommitted")
    End Sub

    Public Overrides Property EditingControlValueChanged As Boolean
        Get
            Return MyBase.EditingControlValueChanged
        End Get
        Set(value As Boolean)
            MyBase.EditingControlValueChanged = value
            'If m_OwnerCell IsNot Nothing Then
            '    m_OwnerCell.Value = Me.SelectedValue
            'End If
            'EditingControlDataGridView.NotifyCurrentCellDirty(True)
        End Set
    End Property

    Protected Overrides Sub OnSelectedIndexChanged(e As EventArgs)
        '
        MyBase.OnSelectedIndexChanged(e)

        'Debug.Print("OnselectedIndexchanged")


        If m_OwnerCell Is Nothing Then
            Return
        End If

        Try
            Dim col As DataGridViewMultiColumnComboBoxColumn = CType(m_OwnerCell.OwningColumn, DataGridViewMultiColumnComboBoxColumn)
            '判断是否设置了第二返回值 
            If col.SelectedIndex2 <> -1 Then
                col.SelectedValue2 = col.Items(Me.SelectedIndex)(col.SelectedIndex2)

                m_OwnerCell.Value = Me.SelectedValue

                EditingControlDataGridView.NotifyCurrentCellDirty(True)
            End If

            '保存当下的选择索引 
            col.SelectedIndex = Me.SelectedIndex

            'Debug.Print("OnselectedIndexchanged - selectedindex2" & col.Items(Me.SelectedIndex)(col.SelectedIndex2))
        Catch ex As Exception


        End Try
    End Sub

    'Private m_ifHotKey As Boolean

    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        'm_ifHotKey = False
        '判断是否是热键
        If e.Control AndAlso e.KeyCode = Keys.F Then
            '此处说明抓到了Ctrol+F
            If OwnerCell.OwningColumn IsNot Nothing Then
                Dim col = CType(OwnerCell.OwningColumn, DataGridViewMultiColumnComboBoxColumn)
                If col IsNot Nothing AndAlso DataGridViewMultiColumnComboBoxColumn.DelegateCallBack IsNot Nothing Then
                    DataGridViewMultiColumnComboBoxColumn.DelegateCallBack.Invoke()
                End If
            End If


            'Debug.Print("editingControl - keydown : ctrl+F")
            'm_ifHotKey = True
            e.Handled = True
            Return
        End If
        '传递处理
        MyBase.OnKeyDown(e)
    End Sub


    Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
        Select Case keyData
            Case Keys.Enter

                Try
                    Dim col As DataGridViewMultiColumnComboBoxColumn = CType(m_OwnerCell.OwningColumn, DataGridViewMultiColumnComboBoxColumn)
                    '判断是否设置了第二返回值 
                    If col.SelectedIndex2 <> -1 Then
                        col.SelectedValue2 = col.Items(Me.SelectedIndex)(col.SelectedIndex2)

                        m_OwnerCell.Value = Me.SelectedValue

                        EditingControlDataGridView.NotifyCurrentCellDirty(True)
                    End If

                    col.SelectedIndex = Me.SelectedIndex

                Catch ex As Exception


                End Try

                SendKeys.Send("{Tab}")

                Return True
        End Select

        Return MyBase.ProcessCmdKey(msg, keyData)
    End Function


    Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
        MyBase.OnDrawItem(e)

        '准备画控件
        '当前控件的 客户区
        Dim rec As Rectangle = e.Bounds
        '设置分隔线的笔刷
        Dim NormalTextBrush As SolidBrush = New SolidBrush(SystemColors.ControlText)


        '取出当前控件所在栏
        Dim ownerColumn As DataGridViewMultiColumnComboBoxColumn = CType(OwnerCell.OwningColumn, DataGridViewMultiColumnComboBoxColumn)
        '取出各栏宽度
        Dim colWidths() As String = Nothing

        If ownerColumn.ColumnWidths IsNot Nothing Then
            colWidths = ownerColumn.ColumnWidths.Split(";"c)
        End If

        '如果有数据，即e.index有正常值
        If e.Index = -1 Then
            Return '没有数据需要画
        End If

        '取出当前列的数据源
        'Dim colDataSource As Object = ownerColumn.DataSource
        Dim colItems As Object = ownerColumn.Items
        Dim curDataView As DataRowView
        Dim currentRow As DataRow = Nothing
        Dim currentItems() As Object
        '取当前行，试着转换为datatable对象
        curDataView = TryCast(Items(e.Index), DataRowView)
        If curDataView IsNot Nothing Then
            currentRow = curDataView.Row
        End If

        If currentRow Is Nothing Then
            Dim iL As IList = CType(Items(e.Index), IList)
            ReDim currentItems(iL.Count - 1)
            For i = 0 To iL.Count - 1
                currentItems(i) = iL.Item(i)
            Next
        Else
            currentItems = currentRow.ItemArray
        End If

        '判断行是否有集合值
        If currentItems Is Nothing Then
            Return

        End If
        '开始画
        '取当前值
        Dim currentText As String = GetItemText(Items(e.Index))

        'Debug.Print("OnDrawItem:currentText=" & currentText)

        '先画普通白色背景
        Dim normalBackBrush As SolidBrush = New SolidBrush(Color.White)
        e.Graphics.FillRectangle(normalBackBrush, rec)

        '判断是否下拉
        If Me.DroppedDown And (Not Margin.Top = rec.Top) Then
            '有下拉，则画显示部分

            '当前偏移
            Dim currentOffset As Integer = rec.Left
            '高亮画刷
            Dim highLightBackBrush As SolidBrush = New SolidBrush(SystemColors.Highlight)
            If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                '被选中背景颜色 
                e.Graphics.FillRectangle(highLightBackBrush, rec)
            End If

            Dim addBorder As Boolean = False
            Dim valueItem As Object

            Dim tmpNumOfField As Integer
            If colWidths IsNot Nothing AndAlso colWidths.Length > 0 Then
                tmpNumOfField = colWidths.Length - 1
            Else
                tmpNumOfField = currentItems.Length - 1
            End If
            'For Each rowItem In currentItems
            'For i = 0 To currentItems.Length - 1
            For i = 0 To tmpNumOfField
                valueItem = currentItems(i)
                Dim value As String = currentItems(i).ToString() 'TODO:应该支持多种数据类型

                'Debug.Print("value=" & value)

                If addBorder Then
                    '画分隔
                    Dim gridBrush As SolidBrush = New SolidBrush(Color.Gray)
                    Dim linesNum As Long = 1 '可以考虑配置
                    While linesNum > 0
                        linesNum -= 1
                        Dim first As Point = New Point(rec.Left + currentOffset, rec.Top)
                        Dim last As Point = New Point(rec.Left + currentOffset, rec.Bottom)
                        e.Graphics.DrawLine(New Pen(gridBrush), first, last)

                        currentOffset += 1
                    End While
                Else
                    addBorder = True
                End If

                '测量宽度
                Dim extent As SizeF = e.Graphics.MeasureString(valueItem, e.Font)
                Dim Width As Decimal = CType(extent.Width, Decimal)
                '如果 太长，则截断
                Dim textRec As Rectangle = New Rectangle(currentOffset, rec.Y, CType(Decimal.Ceiling(Width), Integer), rec.Height)
                '取出当前列设定宽度值，如果未设定，则宽度默认为50
                Dim fixedWidth As Int32
                If String.IsNullOrEmpty(ownerColumn.ColumnWidths) OrElse colWidths.Length < 1 Then
                    fixedWidth = 50
                Else
                    Try
                        fixedWidth = Integer.Parse(colWidths(i))

                    Catch ex As Exception
                        fixedWidth = 0
                        '这里直接到下一个
                        Continue For
                    End Try

                End If

                '开始画 一个值 
                If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                    '被选择的部分
                    Dim HighlightedText As SolidBrush = New SolidBrush(SystemColors.HighlightText)
                    '如果前面部分太长，则重画应当被截断的部分背景
                    e.Graphics.FillRectangle(highLightBackBrush, currentOffset, rec.Y, fixedWidth, extent.Height) '此处应根据设置进行宽度修改
                    '画字串
                    e.Graphics.DrawString(value, e.Font, normalBackBrush, textRec)
                Else
                    '如果前面部分太长，则重画应当被截断的部分背景
                    e.Graphics.FillRectangle(normalBackBrush, currentOffset, rec.Y, fixedWidth, extent.Height) '此处应根据设置进行宽度修改
                    '画字串
                    e.Graphics.DrawString(value, e.Font, NormalTextBrush, textRec)
                End If

                currentOffset += fixedWidth  'fixedAlignColumnSize 设置

            Next
        Else
            '没有下拉，只画当前值
            e.Graphics.DrawString(currentText, e.Font, NormalTextBrush, rec)
        End If
    End Sub

End Class